﻿Imports Aricie.DNN.Services.UpgradeSystem
Imports Aricie.DNN.Services.UpgradeSystem.Actions
Imports Aricie.DNN.Settings
Imports System.Globalization
Imports Aricie.DNN.Entities
Imports Aricie.DNN.Diagnostics
Imports Aricie.Services

Namespace Aricie.DNN.Services.Url.FriendlyUrl.Upgrade
    Public Class UpgradeTo231UpgradeProvider
        Implements IUpgradeActionsProvider(Of FriendlierBusinessController)


        Public Function GetActions() As UpgradeSystem.UpgradeActionsCollection Implements UpgradeSystem.IUpgradeActionsProvider(Of FriendlierBusinessController).GetActions
            Dim upgradeTo231 As New UpgradeActionsCollection(New Version(2, 3, 1))

            upgradeTo231.PortalUpgradeActions.Add(New PortalUpgradeAction(ActionStep.PortalUpgradeStarting) With {.Name = "Migration des friendlier rules", .UpgradeOperation = AddressOf MigrateFriendlierRules})

            Return upgradeTo231

        End Function

        Private Sub MigrateFriendlierRules(ByVal version As Version, ByVal portalId As Integer)

            Dim settings As FriendlierUrlSettings = FriendlierUrlContext.GetFriendlierUrlPortalSettings(portalId)
            If FriendlierUrlConfig.Instance.LogLimitConditions Then
                Dim objPair As New KeyValuePair(Of String, String)("Initial Settings", ReflectionHelper.Serialize(settings).OuterXml)
                Dim objDebug As New DebugInfo("Upgrade FUP", "Before Upgrade", objPair)
                SimpleDebugLogger.Instance.AddDebugInfo(objDebug)
            End If

            'the provider file name has changed, migrate old config.
            MigrateProviders()

            'upgrade aliases

            UpgradeAliases(settings)

            ' default behaviour
            UpgradeStrategy(settings.DefaultBehaviour, settings)
            'settings.DefaultBehaviour.FriendlyRule = FromFormerStrategy(settings.DefaultBehaviour, settings)


            ' tabs specific 
            For Each strategy As FriendlierUrlStrategy In settings.TabSpecificBehaviour.Values
                UpgradeStrategy(strategy, settings)
            Next

            ' modules specific 
            For Each strategy As FriendlierUrlStrategy In settings.ModuleSpecificBehaviour.Values
                'strategy.FriendlyRule = FromFormerStrategy(strategy, settings)
                UpgradeStrategy(strategy, settings)
            Next
            If FriendlierUrlConfig.Instance.LogLimitConditions Then
                Dim objPair As New KeyValuePair(Of String, String)("Upgraded settings", ReflectionHelper.Serialize(settings).OuterXml)
                Dim objDebug As New DebugInfo("Upgrade FUP", "After Upgrade", objPair)
                SimpleDebugLogger.Instance.AddDebugInfo(objDebug)
            End If

            FriendlierUrlContext.Instance.SaveFriendlierUrlPortalSettings(portalId, settings)

        End Sub


        Private Const REWRITE_GROUP_REGEX As String = "(?<Group>" _
                                                & "\[(?<GroupPrefix>[^\[\]]*)?" _
                                                & _
                                                "(?<GroupReplace>\${(?<GroupName>[^}\,]+)(?:\,(?<GroupRewriteType>\w+))?(?:\,%(?<GroupRegex>[^\,]+)%)?})" _
                                                & "(?<GroupSuffix>[^\[\]]*)?\]" _
                                                & "(?<InterGroup>[^\[\]{}]*)?)"


        Private Shared _GroupRegex As New Regex(REWRITE_GROUP_REGEX, RegexOptions.IgnoreCase Or RegexOptions.Singleline)

        Private Shared Sub MigrateProviders()
            Dim oldConfig As FriendlierUrlConfig = FriendlierUrlContext.GetOldConfig()
            Dim currentConfig As FriendlierUrlConfig = FriendlierUrlConfig.Instance
            For Each objDefaultProvider As UrlProviderInfo In oldConfig.DefaultUrlProviders
                If Not currentConfig.DefaultUrlProviders.Exists(Function(objProvider As UrlProviderInfo) objProvider.Name = objDefaultProvider.Name) Then
                    currentConfig.DefaultUrlProviders.Add(objDefaultProvider)
                End If
            Next
            For Each objModuleProviderPair As KeyValuePair(Of String, UrlProviderInfo) In oldConfig.ModuleUrlProviders
                If Not currentConfig.ModuleUrlProviders.ContainsKey(objModuleProviderPair.Key) Then
                    currentConfig.ModuleUrlProviders.Add(objModuleProviderPair.Key, objModuleProviderPair.Value)
                End If
            Next
            FriendlierUrlConfig.Save(currentConfig)
        End Sub


        Private Shared Sub UpgradeAliases(settings As FriendlierUrlSettings)



            For Each aliasPair As KeyValuePair(Of String, String) In settings.RuleAliases
                Dim objRule As FriendlierUrlRule = ConvertAlias(aliasPair.Value, settings)
                settings.Aliases(aliasPair.Key) = objRule
            Next

            settings.RuleAliases = Nothing

        End Sub


        Private Shared Function ConvertAlias(strAlias As String, settings As FriendlierUrlSettings) As FriendlierUrlRule
            Dim dumbStrategy As New FriendlierUrlStrategy
            dumbStrategy.ProviderNames.AddRange(FriendlierUrlConfig.Instance.AvailableUrlrewriters.Keys)
            dumbStrategy.RewriteRule = strAlias
            Return FromFormerStrategy(dumbStrategy, settings)
        End Function


        Public Shared Sub UpgradeStrategy(ByVal strategy As FriendlierUrlStrategy, settings As FriendlierUrlSettings)
            If Not String.IsNullOrEmpty(strategy.RewriteRule) Then
                strategy.FriendlyRule = FromFormerStrategy(strategy, settings)
                strategy.RewriteRule = String.Empty
            End If
            For Each subStrategy As ConditionalEntity(Of SubFriendlierUrlStrategy) In strategy.ConditionalBehaviours
                UpgradeStrategy(subStrategy.Value, settings)
            Next
        End Sub


        Public Shared Function FromFormerStrategy(ByVal strategy As FriendlierUrlStrategy, ByVal settings As FriendlierUrlSettings) As FriendlierUrlRule
            Dim toReturn As New FriendlierUrlRule

            Dim aliasSplit As SortedDictionary(Of Integer, KeyValuePair(Of Integer, AliasOrRule)) = SplitAliases(strategy.RewriteRule, settings)
            Dim index As Integer = 0
            For Each currIdx As Integer In aliasSplit.Keys
                Dim curString As KeyValuePair(Of Integer, AliasOrRule) = aliasSplit(currIdx)
                Dim currRule As String = strategy.RewriteRule.Substring(currIdx, curString.Key)

                If curString.Value = AliasOrRule.Rule Then
                    Dim objRule As FriendlierUrlRule = FromStringRule(currRule, strategy, index)
                    toReturn.Instances.AddRange(objRule.Instances)
                Else
                    If settings.RuleAliases IsNot Nothing AndAlso settings.RuleAliases.ContainsKey(currRule) Then
                        Dim strAliasValue As String = settings.RuleAliases(currRule)
                        Dim objRule As FriendlierUrlRule = ConvertAlias(strAliasValue, settings)
                        settings.Aliases(currRule) = objRule
                    End If
                    Dim aliasGroup As New AliasUrlGroup
                    'aliasGroup.Name = "Alias - " & index.ToString(CultureInfo.InvariantCulture) & " - " & currRule
                    'aliasGroup.Decription = "This is an alias group for the url"
                    aliasGroup.AliasName = currRule
                    toReturn.Instances.Add(aliasGroup)

                End If

                index += 1

            Next

            Return toReturn
        End Function

        Public Shared Function FromStringRule(ByVal oldRule As String, ByVal strategy As FriendlierUrlStrategy, ByRef index As Integer) As FriendlierUrlRule
            Dim toReturn As New FriendlierUrlRule
            For Each objRuleMatch As Match In GetOldRewriteRuleMatches(FriendlierUrlContext.Instance.FriendlierUrlPortalSettings, oldRule)
                Dim interGroup As ConstantUrlGroup = Nothing
                Dim varGroup As VariableUrlGroup = BuildGroup(objRuleMatch, strategy, interGroup, index)
                toReturn.Instances.Add(varGroup)
                index += 1
                If interGroup IsNot Nothing Then
                    toReturn.Instances.Add(interGroup)
                    index += 1
                End If
            Next
            Return toReturn
        End Function

        Public Shared Function GetOldRewriteRuleMatches(ByVal settings As FriendlierUrlSettings, ByVal replacementRule As String) As MatchCollection

            Dim toReturn As MatchCollection = Nothing
            If Not FriendlierUrlContext.Instance.NoCache Then
                toReturn = Aricie.Services.CacheHelper.GetGlobal(Of MatchCollection)(replacementRule)
            End If
            If toReturn Is Nothing Then

                If settings.RuleAliases IsNot Nothing Then
                    For Each entry As KeyValuePair(Of String, String) In settings.RuleAliases
                        replacementRule = replacementRule.Replace(entry.Key, entry.Value)
                    Next
                End If

                toReturn = _GroupRegex.Matches(replacementRule)
                Aricie.Services.CacheHelper.SetCacheDependant(Of MatchCollection)(toReturn, _
                    FriendlierUrlContext.CACHING_DEPENDENCY, Aricie.Constants.Cache.GlobalExpiration, replacementRule)
            End If
            Return toReturn
        End Function

        Private Shared Function BuildGroup(ByVal objRuleMatch As Match, ByVal oldStrategy As FriendlierUrlStrategy, ByRef interConstantGroup As ConstantUrlGroup, ByVal index As Integer) As VariableUrlGroup
            'dechiffrage groupe de réécriture en cours
            Dim toReturn As VariableUrlGroup = Nothing

            Dim interGroup As String = objRuleMatch.Groups("InterGroup").Value
            If Not String.IsNullOrEmpty(interGroup) Then
                interConstantGroup = New ConstantUrlGroup
                ' interConstantGroup.Name = "InterGroup - " & index.ToString(CultureInfo.InvariantCulture)
                ' interConstantGroup.Decription = "This is a constant part in the Url"
                interConstantGroup.Value = interGroup
            End If

            Dim ruleGroupName As String = objRuleMatch.Groups("GroupName").Value
            Dim tokenSepIdx As Integer = ruleGroupName.IndexOf(":"c)
            Dim prop As String = ""
            If tokenSepIdx <> -1 Then
                'token group (special case)
                prop = ruleGroupName.Substring(tokenSepIdx + 1)
                ruleGroupName = ruleGroupName.Substring(0, tokenSepIdx)

                'Me._UrlRewriteInput.Params(UrlParam.FromSyntax(ruleGroupName)) = prop

            End If
            For Each objProvider As UrlProviderInfo In oldStrategy.StrategyProviders
                Dim provider As IUrlRewriterProvider = objProvider.GetProvider
                If provider IsNot Nothing Then
                    For Each groupRewrite As GroupParamsRewrite In provider.GetRewrites
                        If groupRewrite.GroupName.ToLowerInvariant = ruleGroupName.ToLowerInvariant Then
                            Dim provGroup As New ProviderUrlGroup
                            provGroup.GroupName = ruleGroupName
                            '   provGroup.Name = "Provider - " & index.ToString(CultureInfo.InvariantCulture) & " - " & ruleGroupName
                            provGroup.UrlProviderName = objProvider.Name
                            '   provGroup.Decription = groupRewrite.Description
                            'provGroup.EnableFilter = False
                            If prop <> "" Then
                                If groupRewrite.SupportsEntityToken Then
                                    provGroup.EntityToken = prop
                                Else
                                    Throw New ApplicationException(String.Format("Group {0} does not support tokens", ruleGroupName))
                                End If
                            End If
                            Dim strRewriteType As String = objRuleMatch.Groups("GroupRewriteType").Value
                            Dim objRewriteType As RewriteType = RewriteType.Any
                            If strRewriteType <> "" Then
                                objRewriteType = strRewriteType.GetEnum(Of RewriteType)()
                                provGroup.RewriteType = objRewriteType
                            End If
                            toReturn = provGroup
                        End If
                    Next
                End If
            Next
            If toReturn Is Nothing Then


                Dim part As UrlPart

                Try
                    part = CType([Enum].Parse(GetType(UrlPart), ruleGroupName), UrlPart)
                Catch ex As Exception
                    'Querystring
                    Dim queryGroup As New QueryStringUrlGroup
                    queryGroup.QueryStringName = ruleGroupName
                    ' queryGroup.Name = "QueryString - " & index.ToString(CultureInfo.InvariantCulture) & " - " & ruleGroupName
                    '    queryGroup.Decription = "This is a querystring parameter"
                    queryGroup.RewriteType = RewriteType.Any

                    toReturn = queryGroup
                End Try

                If toReturn Is Nothing Then
                    'UrlPart
                    Dim urlPartGroup As New UrlPartGroup
                    urlPartGroup.UrlPartName = part
                    '       urlPartGroup.Name = "UrlPart - " & index.ToString(CultureInfo.InvariantCulture) & " - " & ruleGroupName
                    '      urlPartGroup.Decription = "This is a url parameter"
                    urlPartGroup.RewriteType = RewriteType.Any
                    urlPartGroup.EnableFilter = False
                    toReturn = urlPartGroup
                End If

            End If
            Dim prefix As String = objRuleMatch.Groups("GroupPrefix").Value
            Dim suffix As String = objRuleMatch.Groups("GroupSuffix").Value
            toReturn.Prefix = prefix
            toReturn.Suffix = suffix

            'Dim groupVar As String = objRuleMatch.Groups("GroupVar").Value

            Dim groupRegex As String = objRuleMatch.Groups("GroupRegex").Value
            toReturn.Regex = groupRegex
            Return toReturn

        End Function


        Private Shared Function SplitAliases(ByVal rule As String, ByVal settings As FriendlierUrlSettings) As SortedDictionary(Of Integer, KeyValuePair(Of Integer, AliasOrRule))
            Dim toReturn As New SortedDictionary(Of Integer, KeyValuePair(Of Integer, AliasOrRule))
            If settings.RuleAliases IsNot Nothing AndAlso settings.RuleAliases.Count > 0 Then
                For Each entry As KeyValuePair(Of String, String) In settings.RuleAliases
                    Dim position As Integer = rule.IndexOf(entry.Key, StringComparison.Ordinal)
                    If position > -1 Then
                        toReturn.Add(position, New KeyValuePair(Of Integer, AliasOrRule)(entry.Key.Length, AliasOrRule.Alias))
                    End If
                Next
            ElseIf settings.Aliases IsNot Nothing Then
                For Each entry As KeyValuePair(Of String, FriendlierUrlRule) In settings.Aliases
                    Dim position As Integer = rule.IndexOf(entry.Key, StringComparison.Ordinal)
                    If position > -1 Then
                        toReturn.Add(position, New KeyValuePair(Of Integer, AliasOrRule)(entry.Key.Length, AliasOrRule.Alias))
                    End If
                Next
            End If

            Dim traverser As Integer = 0
            Dim currAliasIdx As Integer = 0
            Dim currAlias As KeyValuePair(Of Integer, AliasOrRule)
            Dim aliases As New List(Of Integer)(toReturn.Keys)
            While currAliasIdx < aliases.Count
                If aliases(currAliasIdx) > traverser Then
                    toReturn.Add(traverser, New KeyValuePair(Of Integer, AliasOrRule)(aliases(currAliasIdx) - traverser, AliasOrRule.Rule))
                    traverser = aliases(currAliasIdx)
                End If
                currAlias = toReturn(traverser)
                traverser += currAlias.Key
                currAliasIdx += 1
            End While
            If traverser < rule.Length Then
                toReturn.Add(traverser, New KeyValuePair(Of Integer, AliasOrRule)(rule.Length - traverser, AliasOrRule.Rule))
            End If
            Return toReturn
        End Function

    End Class

End Namespace
